package net.keebook.webapp.bean;

import java.io.IOException;
import java.util.TimeZone;

import javax.annotation.PostConstruct;
import javax.ejb.EJB;
import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;
import javax.faces.component.UIComponent;
import javax.faces.component.UIInput;
import javax.faces.context.FacesContext;
import javax.inject.Inject;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import net.keebook.webapp.dao.LoginMemoryDao;
import net.keebook.webapp.dao.SettingDao;
import net.keebook.webapp.dao.UserDao;
import net.keebook.webapp.entity.LoginMemory;
import net.keebook.webapp.entity.User;
import net.keebook.webapp.entity.User.LoginMethod;
import net.keebook.webapp.model.SessionModel;
import net.keebook.webapp.util.BCrypt;
import net.keebook.webapp.util.DateUtil;
import net.keebook.webapp.util.MessageBundle;
import net.keebook.webapp.util.Messages;
import net.keebook.webapp.util.SettingId;

import org.apache.log4j.Logger;


@ManagedBean
@RequestScoped
public class LoginBean {
	private String username = "";
	private String password = "";
	private Boolean remember = false;
	@Inject
	private UserDao userDao;
	@Inject
	private SettingDao settingDao;
	@Inject
	private LoginMemoryDao loginMemoryDao;
	@EJB
	private OpenIdBean openIdBean;
	@EJB
	private OAuthBean oAuthBean;
	@EJB
	private LoginSupportBean loginSupportBean;
	private SessionModel sessionModel;
	
	@PostConstruct
	public void init() throws IOException {
		setSessionModel(SessionModel.getInstance(FacesContext.getCurrentInstance()));
		
		HttpServletRequest request = (HttpServletRequest)FacesContext.getCurrentInstance().getExternalContext().getRequest();
		if (request.getParameter("logout") != null && !"".equals(request.getParameter("logout"))) {
			logout();
			return;
		}
		
		if (request.getParameter("sessionTimeout") != null && !"".equals(request.getParameter("sessionTimeout"))) {
			logout(true);
			return;
		}
		
		if (getSessionModel().getLoggedIn())  {
			FacesContext.getCurrentInstance().getExternalContext().redirect("index.xhtml");
			return;
		}
	}
	
	public Boolean getSignupEnabled() {
		return getSettingDao().get(SettingId.ALLOW_SIGNUP).getBoolean();
	}
	
	public Boolean getGoogleLoginEnabled() {
		return getSettingDao().get(SettingId.GOOGLE_LOGIN).getBoolean();
	}
	
	public Boolean getFacebookLoginEnabled() {
		return getSettingDao().get(SettingId.FACEBOOK_LOGIN).getBoolean();
	}
	
	public Boolean getTwitterLoginEnabled() {
		return getSettingDao().get(SettingId.TWITTER_LOGIN).getBoolean();
	}
	
	public Boolean getPasswordLoginEnabled() {
		return getSettingDao().get(SettingId.PASSWORD_LOGIN).getBoolean();
	}
	
	public String getUsername() {
		return username;
	}
	public void setUsername(String username) {
		this.username = username;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
	public Boolean isLoggedIn() {
		return getSessionModel().getLoggedIn();
	}
	
	public void login() throws IOException {
		User user = getUserDao().getUserByUsername(getUsername());
		getSessionModel().clear();
		
		if (user != null && user.getEnabled() && LoginMethod.PASSWORD.equals(user.getLoginMethod()) && BCrypt.checkpw(getPassword(), user.getHashedPassword())) {
			getLoginSupportBean().login(user, getSessionModel());
			
			if (getRemember()) {
				LoginMemory memory = new LoginMemory();
				memory.setUser(user);
				memory.setCreationDate(DateUtil.getUtcTimestamp(TimeZone.getTimeZone(getSettingDao().get(SettingId.SYSTEM_TIMEZONE).getValue())));
				getLoginMemoryDao().store(memory);
				
				Cookie cookie = new Cookie("authKey", memory.getExposableId());
				cookie.setMaxAge(60*60*24*7);
				
				HttpServletResponse response = (HttpServletResponse)FacesContext.getCurrentInstance().getExternalContext().getResponse();
				response.addCookie(cookie);
			}
			
			FacesContext.getCurrentInstance().getExternalContext().redirect(getLoginSupportBean().getRedirectionUrl(getSessionModel()));	
		} else {
			Logger.getLogger(this.getClass()).warn("Login attempt failed for user " + username);
			FacesContext.getCurrentInstance().getExternalContext().redirect("login.xhtml?msg="+Messages.LOGIN_FAILED);
		}
	}
	
	public void validateUserPass(FacesContext context, UIComponent component, Object value) {
		String username = (String)((UIInput)context.getViewRoot().findComponent("loginForm:username")).getValue();
		String password = (String)value;
		
		User user = getUserDao().getUserByUsername(username);
		
		if (!(user != null && user.getEnabled() && LoginMethod.PASSWORD.equals(user.getLoginMethod()) && BCrypt.checkpw(password, user.getHashedPassword()))) {
			Logger.getLogger(this.getClass()).warn("Login attempt failed for user " + username);
			((UIInput)component).setValid(false);
			context.addMessage(component.getClientId(context), new FacesMessage(MessageBundle.get().getString("error.loginFailed")));
		}
	}
	
	public void loginGoogle() throws IOException {
		if (!getGoogleLoginEnabled()) {
			return;
		}
		getoAuthBean().loginGoogle(FacesContext.getCurrentInstance());
	}
	
	public void loginFacebook() throws IOException {
		if (!getFacebookLoginEnabled()) {
			return;
		}
		getoAuthBean().loginFacebook(FacesContext.getCurrentInstance());
	}
	
	public void loginTwitter() throws IOException {
		if (!getTwitterLoginEnabled()) {
			return;
		}
		getoAuthBean().loginTwitter(FacesContext.getCurrentInstance(), getSessionModel());
	}
	
	public void logout(boolean timeout) throws IOException {
		Cookie cookie = new Cookie("authKey", "");
		cookie.setMaxAge(0);
		HttpServletResponse response = (HttpServletResponse)FacesContext.getCurrentInstance().getExternalContext().getResponse();
		response.addCookie(cookie);
		
		getSessionModel().clear();
		
		// Manual redirect required due to bug in Tomcat (cannot set cookie and send redirect in one response)
		// See http://www.jguru.com/faq/view.jsp?EID=53251
		response.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY);
		if (timeout) {
			response.setHeader("Location", "login.xhtml?msg="+Messages.LOGOUT_TIMEOUT);
		} else {
			response.setHeader("Location", "login.xhtml?msg="+Messages.LOGOUT);
		}
	}
	
	public void logout() throws IOException {
		logout(false);
	}
	
	public Boolean getKeeBookSelected() {
		return getSessionModel().getKeeBookSelected();
	}
	public UserDao getUserDao() {
		return userDao;
	}
	public void setUserDao(UserDao userDao) {
		this.userDao = userDao;
	}

	public SettingDao getSettingDao() {
		return settingDao;
	}

	public void setSettingDao(SettingDao settingDao) {
		this.settingDao = settingDao;
	}

	public SessionModel getSessionModel() {
		return sessionModel;
	}

	public void setSessionModel(SessionModel sessionModel) {
		this.sessionModel = sessionModel;
	}

	public Boolean getRemember() {
		return remember;
	}

	public void setRemember(Boolean remember) {
		this.remember = remember;
	}

	public LoginMemoryDao getLoginMemoryDao() {
		return loginMemoryDao;
	}

	public void setLoginMemoryDao(LoginMemoryDao loginMemoryDao) {
		this.loginMemoryDao = loginMemoryDao;
	}

	public OpenIdBean getOpenIdBean() {
		return openIdBean;
	}

	public void setOpenIdBean(OpenIdBean openIdBean) {
		this.openIdBean = openIdBean;
	}

	public OAuthBean getoAuthBean() {
		return oAuthBean;
	}

	public void setoAuthBean(OAuthBean oAuthBean) {
		this.oAuthBean = oAuthBean;
	}

	public LoginSupportBean getLoginSupportBean() {
		return loginSupportBean;
	}

	public void setLoginSupportBean(LoginSupportBean loginSupportBean) {
		this.loginSupportBean = loginSupportBean;
	}
}
